const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

class MyPromise {
  // 命名带下划线为私有变量，防止在实例对象方法外被直接使用、修改或调用（意思一下，虽然并不能防住）

  _status = PENDING // 状态，初始为等待
  _value = undefined // 值
  _reason = undefined // 错误原因
  _successCallbackList = [] // 成功后需要调用的函数
  _failCallbackList = [] // 失败后需要调用的函数

  constructor (executor) {
    // 实现构造方法
    executor(this._resolve, this._reject)
  }

  _resolve = (value) => {
    if (this._status === PENDING) {
      // 当前状态为等待时执行，防止多次修改状态
      this._status = FULFILLED // 修改状态为成功
      this._value = value //设置值
      while (this._successCallbackList.length) {
        this._successCallbackList.shift()()
      }
    }
  }

  _reject = (reason) => {
    if (this._status === PENDING) {
      // 当前状态为等待时执行，防止多次修改代码
      this._status = REJECTED // 修改状态为失败
      this._reason = reason //设置错误原因
      while (this._failCallbackList.length) {
        this._failCallbackList.shift()()
      }
    }
  }

  /**
   * then方法
   * @param successCallback 成功回调
   * @param failCallback  失败回调
   * @returns {MyPromise} 新的Promise
   */
  then = (successCallback, failCallback) => {
    // 当传入的回调函数为空或者不为函数时，将其修改为返回与当前promise相同值与状态的函数
    successCallback = successCallback && successCallback instanceof Function ? successCallback : value => value
    failCallback = failCallback && failCallback instanceof Function ? failCallback : reason => { throw reason }

    // then方法需要返回一个新的MyPromise对象
    let nextPromise = new MyPromise((resolve, reject) => {
      // 当前promise成功后需要做的操作
      const successHandle = () => {
        // 使用定时器使得nextPromise在使用时不为空
        setTimeout(() => {
          try {
            this._handlePromise(nextPromise, successCallback(this._value), resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      // 当前promise成功后需要做的操作
      const failHandle = () => {
        // 使用定时器使得nextPromise在使用时不为空
        setTimeout(() => {
          try {
            this._handlePromise(nextPromise, failCallback(this._reason), resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }

      // 判断当前状态
      switch (this._status) {
        case FULFILLED:
          successHandle()
          break
        case REJECTED:
          failHandle()
          break
        case PENDING:
          // 当前状态为等待时,将需要做的
          this._successCallbackList.push(successHandle)
          this._failCallbackList.push(failHandle)
          break
      }
    })
    return nextPromise
  }

  /**
   * 处理then方法中的新的promise
   * @param promise 新的promise
   * @param result  then方法中对应回调函数的结果
   * @param resolve 新的promise的resolve方法
   * @param reject  新的promise的reject方法
   */
  _handlePromise = (promise, result, resolve, reject) => {
    if (result instanceof MyPromise) {
      // 结果为MyPromise对象时
      if (result === promise) {
        // 当结果与新的promise是同个对象时，会导致循环调用，需要处理异常
        reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        return
      }
      // 新的promise的状态与值以及状态变化与结果同步
      result.then(resolve, reject)
      return
    }
    // 不是MyPromise对象时，直接resolve结果
    resolve(result)
  }

  /**
   * catch方法，等同于不传成功回调的then方法
   * @param failCallback
   */
  catch = (failCallback) => {
    this.then(undefined, failCallback)
  }

  finally = (callback) => {
    /**
     * 将callback的结果转换为MyPromise对象
     *
     * 当callback结果不为MyPromise对象时返回一个与当前对象同步的MyPromise对象
     *
     * 当callback结果为MyPromise对象时
     * 若callback结果终态为fulfilled时返回一个与当前对象同步的MyPromise对象
     * 否则返回一个与callback结果同步的MyPromise对象
     */
    return this.then(
      (value) => {
        return MyPromise.resolve(callback()).then(() => value)
      },
      (reason) => {
        return MyPromise.resolve(callback()).then(() => { throw reason })
      })
  }

  static resolve = (value) => {
    if (value instanceof MyPromise) {
      // 值为Promise对象时直接返回
      return value
    }
    return new MyPromise(resolve1 => {
      resolve1(value)
    })
  }

  static reject = (reason) => {
    return new MyPromise((_, reject1) => {
      reject1(reason)
    })
  }

  static all = (promises) => {
    return new MyPromise((resolve, reject) => {
      const result = [] // 任务结果数组
      let finishNum = 0 // 已完成的任务数
      /**
       * 将任务结果添加到结果数组
       * @param value
       * @param index
       */
      function addData (value, index) {
        result[index] = value
        finishNum++
        if (finishNum === promises.length) {
          // 完成所有任务
          resolve(result)
        }
      }

      promises.forEach((item, index) => {
        // 讲promises中的值全转换为MyPromise对象
        MyPromise.resolve(item)
          .then(data => {
            // 成功时添加到结果数据
            addData(data, index)
          }).catch(reason => {
          // 失败时返回promise结果为失败
          reject(reason)
        })
      })
    })
  }

  static allSettled = (promises) => {
    return new MyPromise((resolve, reject) => {
      const result = [] // 任务结果数组
      let finishNum = 0 // 已完成的任务数
      /**
       * 将任务结果添加到结果数组
       * @param value
       * @param index
       */
      function addData (value, index) {
        result[index] = value
        finishNum++
        if (finishNum === promises.length) {
          // 完成所有任务
          resolve(result)
        }
      }

      promises.forEach((item, index) => {
        // 讲promises中的值全转换为MyPromise对象
        MyPromise.resolve(item)
          .then(data => {
            // 添加数据到结果数据
            addData({value: data, status: FULFILLED}, index)
          }).catch(reason => {
          // 添加数据到结果数据
          addData({reason, status: REJECTED}, index)
        })
      })
    })
  }

  static race = (promises) => {
    return new MyPromise((resolve, reject) => {
      promises.forEach((item, index) => {
        // 讲promises中的值全转换为MyPromise对象
        MyPromise.resolve(item)
          .then(data => {
            resolve(data)
          }).catch(reason => {
          reject(reason)
        })
      })
    })
  }
}

// TEST
// MyPromise.all([1, 2, MyPromise.resolve(3), new MyPromise((resolve) => setTimeout(() => {
//   resolve(4)
// }, 1000)),
//   new MyPromise((resolve) => setTimeout(() => {
//     resolve(4)
//   }, 1000)).then(5),
//   new MyPromise((resolve) => setTimeout(() => {
//     resolve(4)
//   }, 1000)).then(() => MyPromise.resolve(6)),
//   new MyPromise((resolve) => setTimeout(() => {
//     resolve(4)
//   }, 1000)).then(() => 7),
// ]).then(res => {
//   console.log(res)
// })

